package com.example.ai.config;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.example.ai.model.AlibabaOpenAiChatModel;
import com.example.ai.tools.CourseTools;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.ai.autoconfigure.openai.OpenAiChatProperties;
import org.springframework.ai.autoconfigure.openai.OpenAiConnectionProperties;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.model.SimpleApiKey;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.example.ai.contants.SystemConstants.*;

/**
 * 配置类，用于定义和提供应用中常用的Bean
 */
@Configuration
public class CommonConfiguration {

    /**
     * 创建并配置一个通用的ChatClient
     *
     * @param model 用于聊天的模型
     * @return 配置好的ChatClient实例
     */
    @Bean
    public ChatClient chatClient(AlibabaOpenAiChatModel model) {
        return ChatClient.builder(model) // 创建ChatClient工厂
                .defaultOptions(ChatOptions.builder().model("qwen-omni-turbo").build())
                .defaultSystem("你是一个AI智能助手")
                .defaultAdvisors(new SimpleLoggerAdvisor(),
                        new MessageChatMemoryAdvisor(chatMemory()))
                .build(); // 构建ChatClient实例
    }

    /**
     * 创建并配置一个专门用于游戏场景的ChatClient
     *
     * @param model 用于聊天的模型
     * @return 配置好的游戏专用ChatClient实例
     */
    @Bean
    public ChatClient gameChatClient(OpenAiChatModel model) {
        return ChatClient.builder(model) // 创建ChatClient工厂
                .defaultSystem(JAVA_INTERVIEW_SYSTEM_PROMPT)
                .defaultAdvisors(new SimpleLoggerAdvisor(),
                        new MessageChatMemoryAdvisor(chatMemory()))
                .build(); // 构建ChatClient实例
    }

    /**
     * 创建并配置一个专门用于服务场景的ChatClient，支持使用工具
     *
     * @param model       用于聊天的模型
     * @param courseTools 课程工具，用于增强聊天功能
     * @return 配置好的服务专用ChatClient实例
     */
    @Bean
    public ChatClient serviceChatClient(
            AlibabaOpenAiChatModel model,
            ChatMemory chatMemory, CourseTools courseTools) {
        return ChatClient.builder(model)
                .defaultSystem(CUSTOMER_SERVICE_SYSTEM)
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory), // CHAT MEMORY
                        new SimpleLoggerAdvisor())
                .defaultTools(courseTools)
                .build();
    }

    @Bean
    public ChatClient pdfChatClient(
            OpenAiChatModel model,
            ChatMemory chatMemory,
            VectorStore vectorStore) {
        return ChatClient.builder(model)
                .defaultSystem("请根据提供的上下文回答问题，不要自己猜测。")
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory), // CHAT MEMORY
                        new SimpleLoggerAdvisor(),
                        new QuestionAnswerAdvisor(
                                vectorStore, // 向量库
                                SearchRequest.builder() // 向量检索的请求参数
                                        .similarityThreshold(0.5d) // 相似度阈值
                                        .topK(2) // 返回的文档片段数量
                                        .build()
                        )
                )
                .build();
    }

    /**
     * 配置并创建一个 AlibabaOpenAiChatModel 实例，因为原来的构造方法已经被废弃注解废弃了
     *
     * @param commonProperties          通用的 OpenAI 连接属性
     * @param chatProperties            OpenAI 聊天特定的属性
     * @param restClientBuilderProvider RestClient 构建器的提供者
     * @param webClientBuilderProvider  WebClient 构建器的提供者
     * @param toolCallingManager        工具调用管理器
     * @param retryTemplate             重试模板
     * @param responseErrorHandler      响应错误处理器
     * @param observationRegistry       观察注册表的提供者
     * @param observationConvention     观察约定的提供者
     * @return 配置好的 AlibabaOpenAiChatModel 实例
     */
    @Bean
    public AlibabaOpenAiChatModel alibabaOpenAiChatModel(OpenAiConnectionProperties commonProperties, OpenAiChatProperties chatProperties, ObjectProvider<RestClient.Builder> restClientBuilderProvider, ObjectProvider<WebClient.Builder> webClientBuilderProvider, ToolCallingManager toolCallingManager, RetryTemplate retryTemplate, ResponseErrorHandler responseErrorHandler, ObjectProvider<ObservationRegistry> observationRegistry, ObjectProvider<ChatModelObservationConvention> observationConvention) {
        // 确定使用聊天特定的基址、API密钥、项目ID和组织ID，或者回退到通用属性中的值
        String baseUrl = StringUtils.hasText(chatProperties.getBaseUrl()) ? chatProperties.getBaseUrl() : commonProperties.getBaseUrl();
        String apiKey = StringUtils.hasText(chatProperties.getApiKey()) ? chatProperties.getApiKey() : commonProperties.getApiKey();
        String projectId = StringUtils.hasText(chatProperties.getProjectId()) ? chatProperties.getProjectId() : commonProperties.getProjectId();
        String organizationId = StringUtils.hasText(chatProperties.getOrganizationId()) ? chatProperties.getOrganizationId() : commonProperties.getOrganizationId();

        // 准备连接头信息，包含项目ID和组织ID
        Map<String, List<String>> connectionHeaders = new HashMap<>();
        if (StringUtils.hasText(projectId)) {
            connectionHeaders.put("OpenAI-Project", List.of(projectId));
        }

        if (StringUtils.hasText(organizationId)) {
            connectionHeaders.put("OpenAI-Organization", List.of(organizationId));
        }

        // 获取 RestClient 和 WebClient 构建器
        RestClient.Builder restClientBuilder = restClientBuilderProvider.getIfAvailable(RestClient::builder);
        WebClient.Builder webClientBuilder = webClientBuilderProvider.getIfAvailable(WebClient::builder);

        // 构建 OpenAiApi 实例
        OpenAiApi openAiApi = OpenAiApi.builder()
                .baseUrl(baseUrl)
                .apiKey(new SimpleApiKey(apiKey))
                .headers(CollectionUtils.toMultiValueMap(connectionHeaders))
                .completionsPath(chatProperties.getCompletionsPath())
                .embeddingsPath("/v1/embeddings")
                .restClientBuilder(restClientBuilder)
                .webClientBuilder(webClientBuilder)
                .responseErrorHandler(responseErrorHandler)
                .build();

        // 构建 AlibabaOpenAiChatModel 实例
        AlibabaOpenAiChatModel chatModel = AlibabaOpenAiChatModel.builder()
                .openAiApi(openAiApi)
                .defaultOptions(chatProperties.getOptions())
                .toolCallingManager(toolCallingManager)
                .retryTemplate(retryTemplate)
                .observationRegistry((ObservationRegistry) observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
                .build();

        // 确保 chatModel 不为 null
        Objects.requireNonNull(chatModel);

        // 如果有观察约定，则设置到 chatModel
        observationConvention.ifAvailable(chatModel::setObservationConvention);

        // 返回配置好的 chatModel 实例
        return chatModel;
    }


    /**
     * 创建并配置一个向量存储实例，用于存储向量数据
     *
     * @param embeddingModel 用于嵌入数据的模型
     * @return 配置好的向量存储实例
     */
    @Bean
    public VectorStore vectorStore(OpenAiEmbeddingModel embeddingModel) {
        return SimpleVectorStore.builder(embeddingModel).build();
    }

    /**
     * 创建并配置一个RedisChatMemory实例，用于存储聊天记忆
     *
     * @return 配置好的RedisChatMemory实例
     */
    @Bean
    public ChatMemory chatMemory() {
        return new RedisChatMemory();//如果要使用内存实现的聊天记录，请使用InMemoryChatMemory
    }

}
